/**
 * 请求基础实现
 * @author xiufu.wang
 */
import axios from 'axios'
import settle from 'axios/lib/core/settle'
import buildURL from 'axios/lib/helpers/buildURL'
import utils from 'axios/lib/utils'
import deepmerge from 'deepmerge';

const CancelToken = axios.CancelToken;

function paramsSerializer(params) {
    const encode = encodeURIComponent
    if (utils.isURLSearchParams(params)) {
        return params.toString();
    }
    var parts = [];
    utils.forEach(params, function serialize(val, key) {
        if (val === null || typeof val === 'undefined') {
            return;
        }
        if (utils.isArray(val)) {
            key = key + '[]';
        } else {
            val = [val];
        }

        utils.forEach(val, function parseValue(v) {
            if (utils.isDate(v)) {
                v = v.toISOString();
            } else if (utils.isObject(v)) {
                v = JSON.stringify(v);
            }
            parts.push(encode(key) + '=' + encode(v));
        });
    });
    return parts.join('&');
}

export default class BaseRequestActuator {
    // 基础配置
    constructor(config, method, dataField) {
        this.isRequestActuator = true
        this.defaults = config
        this.defaults = config
        this.method = method
        this.source = CancelToken.source()
        this.userConfig = {
            cancelToken: this.source.token
        }
        this.dataField = dataField || 'data'
    }

    timeout(ms) {
        this.userConfig.timeout = ms
        return this
    }

    headers(opt) {
        this.userConfig.headers = opt
        return this
    }

    responseType(opt) {
        this.userConfig.responseType = opt
        return this
    }

    encode() {
        this.userConfig.paramsSerializer = paramsSerializer
        return this
    }

    data(d) {
        this.userConfig.data = d
        return this
    }

    useResponse(fn) {
        this.userConfig.useResponse = fn
        return this
    }

    baseURL(bu) {
        this.userConfig.baseURL = bu
        return this
    }

    transformRequest(fn) {
        const defaultsTransformRequests = this.defaults.transformRequest || []
        this.userConfig.transformRequest = [fn].concat(defaultsTransformRequests)
        return this
    }

    transformResponse(fn) {
        const defaultsTransformResponses = this.defaults.transformResponse || []
        this.userConfig.transformResponse = defaultsTransformResponses.concat([fn])
        return this
    }
    onUploadProgress(fn) {
        this.userConfig.onUploadProgress = fn
        return this
    }

    enableOnlyFirstLevelJson() {
        this.userConfig.onlyFirstLevelJson = true
        return this
    }

    mockData(data) {
        this.userConfig.mockData = data
        return this
    }

    resloveExcetionResponse(fn) {
        this.userConfig.resloveExcetionResponse = fn
        return this
    }

    cancel(resionMessage) {
        this.source.cancel(resionMessage)
        return this
    }

    whenExceptionFn(fn) {
        this.userConfig.whenExceptionFn = fn
        return this
    }

    createAxiosInstance(url, data) {
        const defaultConfig = this.defaults
        const userConfig = this.userConfig
        // userConfig.data 优先
        if (utils.isFormData(userConfig.data) || utils.isFormData(data) ||
            typeof data === 'string' || typeof userConfig.data === 'string'
        ) {
            data = userConfig.data || data
        } else {
            //合并post()和data方法的参数
            data = deepmerge(userConfig.data || {}, data || {})
        }

        if (this.userConfig.onlyFirstLevelJson === true && typeof data === 'object' && data) {
            data = Object.keys(data || {}).reduce((m, field) => {
                if (typeof data[field] === 'object' && data[field]) {
                    m[field] = JSON.stringify(data[field])
                } else {
                    m[field] = data[field]
                }
                return m
            }, {})

        }

        const config = {
            ...defaultConfig,
            ...userConfig,
            headers: {
                ...(defaultConfig.headers || {}),
                ...(userConfig.headers || {}),
            },
            method: this.method,
            url: url,
            [this.dataField]: data
        }

        /**
         * 如果是/mock或者~
         */
        if (config.url.indexOf('/mock') === 0 || config.url[0] === '~') {
            config.baseURL = ''
            if (config.url[0] === '~') {
                config.url = config.url.slice(1)
            }
        }

        // 处理伪mock数据adapter
        if (config.mockData) {
            // 参考 axios/lib/adapters
            config.transformRequest = data => data
            config.adapter = function (adapterConfig) {
                return new Promise(function (resolve, reject) {
                    const request = {
                        body: {
                            ...(adapterConfig.data || {})
                        }
                    }
                    const response = {
                        $delay: function (timeout, useData) {
                            setTimeout(() => {
                                this.json(useData)
                            }, timeout)
                        },
                        json: function (data) {
                            var response = {
                                data: data,
                                headers: {},
                                config: adapterConfig,
                                request: request
                            };
                            settle(resolve, reject, response);
                        }
                    }
                    if (typeof userConfig.mockData === 'function') {
                        userConfig.mockData(request, response)
                    } else {
                        response.json(userConfig.mockData)
                    }
                })
            }
        }

        // 模拟window.open
        if (this.userConfig.windowOpen && this.method.toLocaleLowerCase() === 'get') {
            config.adapter = function (adapterConfig) {
                return new Promise(function (resolve, reject) {
                    resolve({
                        data: {},
                        headers: {},
                        config: {},
                        request: {}
                    })
                    //打开下载
                    if (window && window.open) {
                        const proxyUrl = (config.baseURL + adapterConfig.url).replace('//', '/')
                        window.open(buildURL(proxyUrl, adapterConfig.params, null))
                    }
                })
            }
        }

        //this.userConfig.useResponse = fn
        const useResponse = userConfig.useResponse
        let useResult = null
        if (typeof useResponse === 'function' && (useResult = useResponse())) {
            return Promise.resolve({
                response: {
                    data: useResult
                },
                err: null
            })
        }

        return axios.create(config).request(config).then((result) => { // eslint-disable-line
            const err = config.resloveExcetionResponse ? config.resloveExcetionResponse(result, false) : null
            if (err) {
                config.whenExceptionFn && config.whenExceptionFn(err)
                return {
                    err: err
                }
            }
            return {
                response: result
            }
        }).catch((err) => { // eslint-disable-line
            err = config.resloveExcetionResponse ? config.resloveExcetionResponse(err, true) : err
            config.whenExceptionFn && config.whenExceptionFn(err)
            return {
                err: err
            }
        })
    }

    //具体执行
    done(url, data) { // eslint-disable-line
        return this.createAxiosInstance(url, data)
    }
}